package com.gitee.sop.support.message;


import lombok.Data;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
import java.util.Optional;
import java.util.Set;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * 负责构建错误消息
 *
 * @author 六如
 */
public class OpenMessageFactory {

    static Logger logger = Logger.getLogger(OpenMessageFactory.class.getName());

    private static final String SOLUTION = ".solution";
    private static final String I18N_ROOT = "i18n";

    private OpenMessageFactory() {
    }

    public static final String SYS_ERR = "系统错误";

    private static final Set<String> noModuleCache = new HashSet<>();


    /**
     * 错误信息的国际化信息
     */
    private static MessageSourceAccessor errorMessageSourceAccessor;

    public static void initMessage() {
        ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader();
        URL i18nFolder = contextClassLoader.getResource(I18N_ROOT);
        if (i18nFolder == null) {
            return;
        }

        try {
            Path path = Paths.get(i18nFolder.toURI());
            try (Stream<Path> walk = Files.walk(path)) {
                Optional<Path> i18nRoot = walk.findFirst();
                if (!i18nRoot.isPresent()) {
                    return;
                }
                File root = i18nRoot.get().toFile();
                List<FileTree> fileTrees = buildFileTree(root);
                Set<String> isvModuleList = buildIsvModuleList(fileTrees);
                OpenMessageFactory.initMessageSource(new ArrayList<>(isvModuleList));
            }
        } catch (URISyntaxException | IOException e) {
            logger.warning("初始化i18n模块错误:" + e.getMessage());
            throw new RuntimeException(e);
        }
    }

    private static Set<String> buildIsvModuleList(List<FileTree> fileTreeList) {
        if (fileTreeList == null) {
            return Collections.emptySet();
        }

        Set<String> isvModuleList = new HashSet<>();

        List<FileTree> leafList = fileTreeList.stream().filter(FileTree::isLeaf).collect(Collectors.toList());
        for (FileTree fileTree : leafList) {
            LinkedList<String> nameList = new LinkedList<>();
            appendNames(nameList, fileTree);
            String moduleName = String.join("/", nameList);
            isvModuleList.add(moduleName);
        }

        return isvModuleList;
    }

    private static void appendNames(LinkedList<String> nameList, FileTree fileTree) {
        nameList.addFirst(fileTree.getName());
        FileTree parent = fileTree.getParent();
        if (parent != null) {
            appendNames(nameList, parent);
        }
    }

    private static List<FileTree> buildFileTree(File file) {
        List<FileTree> fileTrees = new ArrayList<>();
        appendFileTree(fileTrees, file, null);
        return fileTrees;
    }

    private static void appendFileTree(List<FileTree> fileTrees, File file, FileTree parent) {
        FileTree i18nTree = new FileTree();
        i18nTree.setParent(parent);

        fileTrees.add(i18nTree);
        if (file.isDirectory()) {
            i18nTree.setName(file.getName());
            File[] files = file.listFiles();
            if (files == null || files.length == 0) {
                return;
            }
            for (File childFile : files) {
                appendFileTree(fileTrees, childFile, i18nTree);
            }
        } else {
            // i18n/isp/bizerror_en.properties
            String name = file.getName();
            int i = name.indexOf("_");
            if (i < 0) {
                return;
            }
            String module = name.substring(0, i);

            i18nTree.setName(module);
            i18nTree.setLeaf(true);
        }
    }

    /**
     * 设置国际化资源信息
     */
    public static void initMessageSource(List<String> isvModules) {
        HashSet<String> baseNamesSet = new HashSet<>();

        if (isvModules != null) {
            baseNamesSet.addAll(isvModules);
        }

        String[] totalBaseNames = baseNamesSet.toArray(new String[0]);

        logger.info("加载错误码国际化资源：" + StringUtils.arrayToCommaDelimitedString(totalBaseNames));
        ResourceBundleMessageSource bundleMessageSource = new ResourceBundleMessageSource();
        bundleMessageSource.setBasenames(totalBaseNames);
        MessageSourceAccessor messageSourceAccessor = new MessageSourceAccessor(bundleMessageSource);
        setErrorMessageSourceAccessor(messageSourceAccessor);
    }

    /**
     * 通过ErrorMeta，Locale，params构建国际化错误消息
     *
     * @param openError 错误信息
     * @param locale    本地化
     * @param params    参数
     * @return 如果没有配置国际化消息，则直接返回errorMeta中的信息
     */
    public static OpenMessage getMessage(I18nMessage openError, Locale locale, Object... params) {
        if (locale == null) {
            locale = Locale.SIMPLIFIED_CHINESE;
        }
        String subCode = openError.getConfigKey();
        // isp.unknow-error=Service is temporarily unavailable
        String subMsg = getErrorMessage(subCode, locale, params);
        if (ObjectUtils.isEmpty(subMsg)) {
            subMsg = SYS_ERR;
        }
        // isp.unknow-error.solution=Service is temporarily unavailable
        String solution = getErrorMessage(subCode + SOLUTION, locale, params);
        if (ObjectUtils.isEmpty(solution)) {
            solution = "";
        }
        return new DefaultOpenMessage(subCode, subMsg, solution);
    }


    private static void setErrorMessageSourceAccessor(MessageSourceAccessor errorMessageSourceAccessor) {
        OpenMessageFactory.errorMessageSourceAccessor = errorMessageSourceAccessor;
    }

    /**
     * 返回本地化信息
     *
     * @param module 错误模块
     * @param locale 本地化
     * @param params 参数
     * @return 返回信息
     */
    private static String getErrorMessage(String module, Locale locale, Object... params) {
        if (noModuleCache.contains(module)) {
            return null;
        }
        try {
            return errorMessageSourceAccessor.getMessage(module, params, locale);
        } catch (Exception e) {
            noModuleCache.add(module);
            return null;
        }
    }

    @Data
    private static class FileTree {
        private String name;

        private FileTree parent;

        private boolean isLeaf;

        @Override
        public String toString() {
            return "FileTree{" +
                    "name='" + name + '\'' +
                    '}';
        }
    }


}
